; Made by MessiahAndrw
; I just removed some code an added the MikeOS start point
org 32768 ; start point
; org 0x0100 ; dos
use16


; uninitialised data
seed equ sectorend ; the random seed, loaded in from the time
map equ seed + 2 ; 200 bytes (dd * 100 lines) to store the map coordinates in

; constants
c_roadWidth equ 300 ; width of the road
c_roadLeft equ -160 ; coordinate at which everything before is a tree, after may be a fence
c_roadRight equ 160 ; coordinate at which everything after is a tree, before may be a fence
c_screenWidth equ 320
c_endOfMap equ map + (99 * 2)


; Start of the code
start:
	cli
	push 0xa000
	pop fs

	push 0x9000 ; the back buffer
	pop gs

	fninit

	; set up vga
	mov ax, 0x0013 ; 320 width, 200 height
	int 0x10

	mov ah, 0 ; load a random seed from the time
 	int 0x1a
	mov word [seed], dx ; dx

	; loop through the map and set random values for each line
	mov ax, 99
	.1:
		pusha
		call generate_line
		popa
		dec ax
		jns .1 ; loop if lines remain (jump if ax is 0)
	.2:

; draw the screen, doesn't double buffer so it would flicker I imagine
draw:
	; clear the screen
	mov di, 320 * 200
	.1:
		dec di

		mov al, 0 ; set pixel on backbuffer with black while reading it
		xchg byte al, [gs: di]
		mov byte [fs: di], al ; set read pixel onto the front buffer
		
		jnz .1 ; loop if pixels remain
	.2:

	mov bx, draw_line
	mov ax, 99
	mov dx, 199

	; Now draw the terrain (the road and horizon)
	push 159 ; draw left side of the road
	push ax
	push 159 - (c_roadWidth / 2)
	push dx
	mov cl, 8
	call bx


	push 160 ; draw right side of the road
	push ax
	push 160 + (c_roadWidth / 2)
	push dx
	call bx


	push 0 ; draw horizon
	push ax
	push 319
	push ax
	mov cl, 2
	call bx

	; Now render the objects, no reason to have a label here but
	; to divide the terrain and object rendering code. It starts renders line 1-99,
	; while shifting the previous lines (0-98) down, making it animate. Then we'll
	; randomize the top line.
render_objects:
	mov ax, 100 ; ax will store the line number
	mov di, map ; di will store a pointer to the map, that will increment along with ax
	.1:
		mov dx, bx
		mov bx, [di]
		mov [di], dx ; copy up one value
		add di, 2 ;increment di

		pusha
		push bx ; x parameter for draw_image
		push ax ; y parameter for draw_image

		; test to see if this line's object is outside of the road (in which case it'll be a tree)
		; or inside the road (a fence)


		cmp bx, c_roadLeft ; make sure value is on the road
		jle .2
		cmp bx, c_roadRight
		jge .2
			mov di, image_fence
			jmp .3
		.2:
			mov di, image_tree
		.3:
		call draw_image
		popa

		dec ax ; increment ax/line pointer
		
		cmp di, c_endOfMap ; loop if ax <= 100
		jle .1


	; now generate a random value for the top line since everything has scrolled down
	xor ax, ax ; the line no (0)
	call generate_line

	; TODO: Put a delay in right here so it doesn't loop as fast as it can.

	jmp draw ; redraw the screen
	

; plot a pixel
;  ax = x coordinate
;  bx = y coordinate
;  cl = colour
; registers trashed: ax, bx, di
plot_pixel:
	; only x has to be checked if it's in bounds, since no situation will occure
	; when y will be out of bounds

	cmp di, 0 ; make sure x >= 0
	jl .1

	cmp di, c_screenWidth ; make sure x < 320
	jge .1

	cwd

	imul bx, 320
	add di, bx ; calculate pixel pos

	mov byte [gs:di], cl ; copy pixel

.1:
	ret



; Draw a line
;   cl = colour
;   parameters
	p_x1 equ bp + 8 ; starting X position
	p_y1 equ bp + 6 ; starting Y position
	p_x2 equ bp + 4 ; ending X position
	p_y2 equ bp + 2 ; ending Y position
; locals
	l_deltaX equ bp - 2 ; delta X
	l_temp equ bp - 4 ; temporary
draw_line:
	mov bp, sp
	sub sp, 6
	pusha

	; the line is rendered from left to right, so test to make sure the
	; starting coordinates are on the left, otherwise swap x1/y1 with x2/y2
	mov ax, [p_x1] ; test if x1 <= x2
	cmp ax, [p_x2]
	jle .1
		; else, x1 > x2, so swap x1,y1 with x2,y2
		xchg ax, [p_x2]
		mov [p_x1], ax

		mov ax, [p_y1]
		xchg ax, [p_y2]
		mov [p_y1], ax
	.1:

	mov ax, [p_x2] ; calculate delta x
	sub ax, [p_x1]
	mov [l_deltaX], ax

	; if deltaX is 0 then this is a vertical line (if we don't catch this, when we
	; calculate the slop later we'll divide by zero), so this is handled specially
	cmp ax, 0 ; test if deltaX is 0
	jne .2
		; The line will be rendered from top to bottom, so if y2 < y1, then
		; swap the two values. The x coordinates don't need to be since only
		; x1 is referenced.
		mov si, [p_y1] ; test if y1 <= y2
		cmp si, [p_y2]
		jle .11
			; else, y1 > y2, so swap y1 with y2
			xchg si, [p_y2]

		.11: ; scroll down the line and loop through each pixel
			mov di, [p_x1]
			mov bx, si
			call plot_pixel ; plot pixel

			inc si

			cmp si, [p_y2] ; jump if y1 < y2
			jbe .11

		mov sp, bp
		ret 8

	.2:

	mov ax, [p_y2] ; calculate delta y (to calculate slope below)
	sub ax, [p_y1]
	mov [l_temp], ax

	; Calculate the slope, done on the fpu so it doesn't snap to
	; an integer
	fild word [l_temp] ; divide delta y by delta x
	fidiv word [l_deltaX]

	; mov word [l_iterX], 0
	mov si, [l_deltaX]

	; Loop horizontally through the line through delta x. For each x coordinate
	; the y position will be calculated based on the slope. This method only
	; looks good with the slope is >= 1 (that is the line is wider than it is taller).
	; The alternative is to handle a special case for slope < 1, but that would add more
	; code. This is an acceptable limitation if I design the graphics with this in mind.
	.3:
		; duplicate top stack
		fld st0
		mov [l_temp], si
		fimul word [l_temp] ; calculate y pos (m * iterX + y1)
		fiadd word [p_y1]
		fistp word [l_temp] ; move from fpu into register
		mov bx, [l_temp]

		mov di, [p_x1] ; calculate x pos (x1 + iterX)
		add di, si
		call plot_pixel ; plot this dot

		dec si

		jns .3

	popa
	mov sp, bp
	ret 8


; Draws an image. The image is scaled based on it's Y coordinate (to imitate depth - disappearing at the horizon),
; being full sized when at the bottom of the screen/closest.
; di = address of image to draw
; reigsters trashed: ax, bx, dx, di, bp
; parameters
	p_x equ bp + 4 ; X coordinate to render at, 0 = centre, 99 = right
	p_y equ bp + 2 ; Y coordinate to render at, 100 = centre, 0 = bottom
; locals
	l_lines equ bp - 4
	l_lastX equ bp - 6
	l_lastY equ bp - 8
	l_thisX equ bp - 10
	l_thisY equ bp - 12
	l_colour equ bp - 14
	; l_shapes equ bp - 16
draw_image:
	mov bp, sp
	sub sp, 16

	mov al, [di] ; read shapes from image
	cbw
	mov si, ax
	inc di


	.1: ; loop through each shape
		mov al, [di] ; read lines from image
		cbw
		mov cx, ax ; [l_lines], ax
		inc di

		mov al, [di] ; read colour from image
		mov [l_colour], ax
		inc di

		call read_location_x
		mov [l_lastX], ax

		call read_location_y
		mov [l_lastY], ax

		.2: ; loop through each line
			call read_location_x
			mov [l_thisX], ax

			call read_location_y
			mov [l_thisY], ax

			pusha

			push word [l_lastX] ; draw this line
			push word [l_lastY]
			push word [l_thisX]
			push word [l_thisY]
			mov cl, [l_colour]
			call draw_line

			popa

			mov ax, [l_thisX]
			mov [l_lastX], ax

			mov ax, [l_thisY]
			mov [l_lastY], ax

			dec cx

			jnz .2 ; loop if lines remain

		dec word si ; decrement shapes
		
		jnz .1 ; loop if shapes remain

	mov sp, bp
	ret 4

; Helper function to calculate the X coordinate and return it in ax
read_location_x:
	mov al, [di] ; read from the image
	inc di
	cbw

	add ax, [p_x] ; add the X position of the image
	cwd

	idiv word [p_y]

	add ax, 320 / 2 ; add half the screen width, since 0
			; in the coordinates is the centre of the screen
			; (since it's scaling in from the middle)

	ret

; Helper function to calculate the Y coordinate and return it in ax
read_location_y:
	mov al, [di] ; read from the image
	inc di
	cbw

	add ax, 100
	sub ax, [p_y] ; add Y coordinate

	cwd

	idiv word [p_y]

	add ax, 100 ; add 100, to shift it down to the middle of the screen

	ret

; generates an object on a line of the map
; ax = the line to generate (0 - 99)
; trashes bx, ax, di, dx
generate_line:
	shl ax, 1
	add ax, map
	mov di, ax

	imul ax,[seed],0x8405
	inc ax

	mov word [seed], ax

	xor ax, 0x7FC0 ; 7F80
	stosw ; stores ax in es:di
	ret


; ---------------------------------- IMAGES
; The images are stored in vector format (list of lines) so they are
; scalable and small. Images are made up of arrays of bytes.
; The structure is:
; - List of shapes.
;    For each shape:
;      - Number of lines
;      - Colour
;          For each line + 1:
;            - X coordinate
;            - Y coordinate
; 0,0 is the centre of the sprite. Y coordinates shouldn't have
; a positive value since that would give the illusion they're sticking
; into the ground rather than up from it.

; a tree image, basically looks like:
;      /\
;     /  \
;    /_  _\
;     /  \  - light green
;    /    \
;   /______\
;     |  |  - brown
;     |__|
image_tree:
	db 2 ; tree has 2 shapes

	db 3 ; 3 lines
	db 6 ; brown

	db -8
	db -32

	db -8
	db 0

	db 8
	db 0

	db 8
	db -32

	db 7 ; 7 lines
	db 10 ; light green

	db 0 ; left side
	db -127

	db -50
	db -80

	db -25
	db -80
	
	db -75
	db -32

	db 75 ; connect to right
	db -32

	db 25
	db -80

	db 50
	db -80

	db 0
	db -127

; a fence image, basically looks like:
;
;  _______
; |_______|
; |_______| - yellow
; |_|   |_|
image_fence: ; 29 bytes
	db 1 ; fence has 1 shape

	db 12 ; 12 lines
	db 14 ; yellow

	db 57
	db -24

	db 57
	db 0

	db 63
	db 0

	db 63
	db -64

	db -64
	db -64

	db -64
	db -44

	db 63
	db -44

	db 63
	db -24

	db -64
	db -24

	db -64
	db -44

	db -64
	db 0

	db -58
	db 0

	db -58
	db -24

times 510- ($-start)  db 0
dw 0xaa55 ; boot sector

sectorend:

; There are 100 lines of terrain on the screen, for each line 'map' contains a value
; representing an X coordinate of an object on that line (so that there is exactly
; one object per line). If this value is off the road (there is a 19/20 chance it
; will be <-160 || >160) then a tree will be drawn, if it's on the road (1/20 chance)
; then a fence/road block will be drawn.
;
; On each frame every value will be moved down one line, with a new value generated
; for the first line.